package com.macro.mall.service.impl;

import com.macro.mall.dao.OpinionAccountDao;
import com.macro.mall.dto.*;
import com.macro.mall.enums.AccountStateEnum;
import com.macro.mall.enums.IdentityEnum;
import com.macro.mall.exception.ApiException;
import com.macro.mall.feign.SmsFeignClient;
import com.macro.mall.feign.WxFeignClient;
import com.macro.mall.mapper.OpinionAccountMapper;
import com.macro.mall.model.OpinionAccount;
import com.macro.mall.model.OpinionAccountExample;
import com.macro.mall.service.LoginService;
import com.macro.mall.util.EncryptionUtil;
import com.macro.mall.util.Idworker;
import com.macro.mall.util.StringUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

import static com.macro.mall.util.CommonConfig.PHONE_SMS;
import static com.macro.mall.util.CommonConfig.SEND_NUMBER;

@Service
@Transactional(rollbackFor = Exception.class)
public class LoginServiceImpl implements LoginService {

    @Resource
    private SmsFeignClient smsFeignClient;

    @Resource
    private OpinionAccountMapper opinionAccountMapper;

    @Resource
    private OpinionAccountDao opinionAccountDao;

    @Value("${wechatId}")
    private String wechatId;

    @Value("${wechatSecret}")
    private String wechatSecret;

    @Resource
    private Idworker idworker;

    @Resource
    private WxFeignClient wxFeignClient;

    @Override
    public void sendSms(SendSmsReq req) {
        if (StringUtil.isNotMobile(req.getPhone())) {
            throw new ApiException("1320203");
        }

        synchronized (req.getPhone()) {
            VerCodeDto verCode = PHONE_SMS.get(req.getPhone());
            if(verCode != null){
                throw new ApiException("1320204");
            }
            Integer number = SEND_NUMBER.get(req.getPhone());
            if(number == null){
                number = 0;
            }
            if(number >= 30){
                throw new ApiException("1320205");
            }

            Integer verCodeNumber = ThreadLocalRandom.current().nextInt(8999) + 1000;
            LocalDateTime ldt = LocalDateTime.now();
            verCode = new VerCodeDto();
            verCode.setVerCode(verCodeNumber);
            verCode.setLocalDateTime(ldt.plusMinutes(30));
//            smsFeignClient.sendSms("", "");
            SEND_NUMBER.put(req.getPhone(), number + 1);
            PHONE_SMS.put(req.getPhone(), verCode);
        }
    }

    @Override
    public String phoneLogin(WxPhoneLoginReq req) {
        if (StringUtil.isNotMobile(req.getPhone())) {
            throw new ApiException("1320203");
        }
        VerCodeDto verCodeDto = PHONE_SMS.get(req.getPhone());
        if(verCodeDto == null){
            throw new ApiException("1320206");
        }

        if(!req.getVerCode().equals(verCodeDto.getVerCode())){
            throw new ApiException("1320208");
        }

        if(verCodeDto.getLocalDateTime().compareTo(LocalDateTime.now()) <= 0){
            throw new ApiException("1320207");
        }

        Code2Session code2Session = getOpenIdByCode(req.getWechatCode());

        OpinionAccountExample openIdEx = new OpinionAccountExample();
        openIdEx.createCriteria().andOpenIdEqualTo(code2Session.getOpenid()).andIsDeleteEqualTo(Boolean.FALSE);
        List<OpinionAccount> openIdList = opinionAccountMapper.selectByExample(openIdEx);

        // 做业务
        OpinionAccountExample example = new OpinionAccountExample();
        example.createCriteria().andPhoneEqualTo(req.getPhone());
        List<OpinionAccount> list = opinionAccountMapper.selectByExample(example);
        if(list.isEmpty()){
            if(openIdList.size() > 0){
                opinionAccountDao.updateOpenId(code2Session.getOpenid());
            }

            // 做注册
            OpinionAccount insertAccount = new OpinionAccount();
            insertAccount.setId(idworker.nextId());
            insertAccount.setPhone(req.getPhone());
            insertAccount.setIdentity(IdentityEnum.GENERAL.getCodeNum());
            insertAccount.setState((byte)1);
            insertAccount.setOpenId(code2Session.getOpenid());
            insertAccount.setUnionId(code2Session.getUnionid());
            opinionAccountMapper.insert(insertAccount);
            return EncryptionUtil.aesEncrypt(insertAccount.getId());
        }

        OpinionAccount opinionAccount = list.get(0);
        if(!opinionAccount.getOpenId().equals(code2Session.getOpenid()) && openIdList.size() > 0){
            opinionAccountDao.updateOpenId(code2Session.getOpenid());
        }
        if(!AccountStateEnum.OK.getCodeNum().equals(opinionAccount.getState())){
            throw new ApiException("1320209");
        }
        OpinionAccount updateAccount = new OpinionAccount();
        updateAccount.setId(opinionAccount.getId());
        updateAccount.setOpenId(code2Session.getOpenid());
        updateAccount.setUnionId(code2Session.getUnionid());
        opinionAccountMapper.updateByPrimaryKey(updateAccount);
        return EncryptionUtil.aesEncrypt(updateAccount.getId());
    }

    @Override
    public ShowLoginInfoResp info(Long accountId) {
        OpinionAccount opinionAccount = opinionAccountMapper.selectByPrimaryKey(accountId);
        if(opinionAccount != null && opinionAccount.getState().equals(AccountStateEnum.OK.getCodeNum())){
            ShowLoginInfoResp resp = new ShowLoginInfoResp();
            resp.setIdentity(opinionAccount.getIdentity());
            resp.setHeadPortrait(opinionAccount.getHeadPortrait());
            resp.setNickName(opinionAccount.getNickName());
            resp.setPhone(opinionAccount.getPhone());
            resp.setDomain(opinionAccount.getDomain());
            resp.setIntroduction(opinionAccount.getIntroduction());
            return resp;
        }
        return null;
    }

    @Override
    public void updateInfo(Long accountId, UpdateInfoReq req) {
        OpinionAccount opinionAccount = opinionAccountMapper.selectByPrimaryKey(accountId);
        if(opinionAccount != null){
            OpinionAccount update = new OpinionAccount();
            update.setId(accountId);
            if(StringUtil.isNotEmpty(req.getHeadPortrait())){
                update.setHeadPortrait(req.getHeadPortrait());
            }
            if(StringUtil.isNotEmpty(req.getNickName())){
                update.setNickName(req.getNickName());
            }
            update.setUpdateTime(new Date());
            opinionAccountMapper.updateByPrimaryKey(update);
        }
    }

    private WxAccessTokenReturn wxAccessTokenReturn = new WxAccessTokenReturn();

    private String getAccessToken(){
        String accessToken = wxAccessTokenReturn.getAccess_token();
        if(accessToken == null){
            return requestAccessToken();
        }
        Long time = wxAccessTokenReturn.getExpires_in();
        if(time <= LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"))){
            return requestAccessToken();
        }
        return accessToken;
    }

    private String requestAccessToken(){
        synchronized (this){
            wxAccessTokenReturn = wxFeignClient.getAccessToken(wechatId, wechatSecret);
            LocalDateTime ldt = LocalDateTime.now();
            wxAccessTokenReturn.setExpires_in(ldt.toEpochSecond(ZoneOffset.of("+8")) + wxAccessTokenReturn.getExpires_in());
            return wxAccessTokenReturn.getAccess_token();
        }
    }

    private String getPhone(String code){
        WxGetPhoneReturn phoneReturn = wxFeignClient.getPhone(new WxGetPhoneBody(code), getAccessToken());
        if(phoneReturn != null && phoneReturn.getPhone_info() != null && StringUtil.isNotEmpty(phoneReturn.getPhone_info().getPhoneNumber())){
            return phoneReturn.getPhone_info().getPhoneNumber();
        }
        return null;
    }

    private Code2Session getOpenIdByCode(String wechatCode) {
        Code2Session code2Session = wxFeignClient.code2Session(wechatId, wechatSecret, wechatCode);
        if (!"0".equals(code2Session.getErrcode())) {
            throw new ApiException("1320201", "获取微信信息失败,请重试");
        }
        return code2Session;
    }
}
